// Package generics
// desc: 泛型
// author: Locker
// date: 2024/4/9
// version: 1.0.0
package generics

import (
	"encoding/json"
	"reflect"
	"time"
)

var (
	TypeUInt    = reflect.TypeOf((*uint)(nil)).Elem()
	TypeUInt8   = reflect.TypeOf((*uint8)(nil)).Elem()
	TypeUInt16  = reflect.TypeOf((*uint16)(nil)).Elem()
	TypeUInt32  = reflect.TypeOf((*uint32)(nil)).Elem()
	TypeUInt64  = reflect.TypeOf((*uint64)(nil)).Elem()
	TypeInt     = reflect.TypeOf((*int)(nil)).Elem()
	TypeInt8    = reflect.TypeOf((*int8)(nil)).Elem()
	TypeInt16   = reflect.TypeOf((*int16)(nil)).Elem()
	TypeInt32   = reflect.TypeOf((*int32)(nil)).Elem()
	TypeInt64   = reflect.TypeOf((*int64)(nil)).Elem()
	TypeFloat32 = reflect.TypeOf((*float32)(nil)).Elem()
	TypeFloat64 = reflect.TypeOf((*float64)(nil)).Elem()
	TypeByte    = reflect.TypeOf((*byte)(nil)).Elem()
	TypeRune    = reflect.TypeOf((*rune)(nil)).Elem()
	TypeBool    = reflect.TypeOf((*bool)(nil)).Elem()
	TypeString  = reflect.TypeOf((*string)(nil)).Elem()
	TypeUintptr = reflect.TypeOf((*uintptr)(nil)).Elem()

	TypeByteSlice = reflect.TypeOf(([]byte)(nil))
	TypeTime      = reflect.TypeOf((*time.Time)(nil)).Elem()
)

// temp interface
type TempInterface interface {
	Value()
}

var typeTempInterface = reflect.TypeOf((*TempInterface)(nil)).Elem()

// PrintTypeName 输出类型信息
func PrintTypeName[T any](t T) string {
	val := reflect.ValueOf(t)

	if val.Type().Implements(typeTempInterface) {
		return val.String()
	}

	switch val.Kind() {
	case reflect.String:
		return val.Convert(TypeString).String()
	case reflect.Int64:
		return val.Convert(TypeInt64).String()
	case reflect.Slice, reflect.Array:
		if val.Type().ConvertibleTo(TypeByteSlice) {
			return val.Convert(TypeByteSlice).String()
		}
		s, _ := json.Marshal(val.Interface())
		return string(s)
	case reflect.Struct:
		if val.CanConvert(TypeTime) {
			return val.Convert(TypeTime).String()
		}
		s, _ := json.Marshal(val.Interface())
		return string(s)
	case reflect.Map:
		s, _ := json.Marshal(val.Interface())
		return string(s)
	default:
		return val.String()
	}
}
